Workshop: Interpreter and FormFunction/APIFunction

Wolfram Cloud Platform

Carlo Barbieri, Riccardo Di Virgilio

Wolfram Research, Inc.

Version 10 introduced a two closely related functions that are key to the development of rich webapps: FormFunction and APIFunction. We like to think of these two functions as two aspects of the same process, where one is the interface for humans and the other is the corresponding interface for machines.
In this workshop we will touch the techniques needed to build webapps with them and we will explore a piece of underlying technology, the Interpreter superfunction.

Connect to the wifi

WolframTraining

Get this document first

http://wolfr.am/form-workshop

FormObject

$Permissions="Public";

To build Forms and APIs we’ve come up with an object that fully contains the specification of the form and the methods needed to visualize it and transform the data: FormObject.

form = FormObject[{
"quantity" -> "StructuredQuantity", 
"real" -> "Number",
"phone" -> "PhoneNumber"
}]

By looking at the InputForm of  FormObject one can see the canonical form which can be used to add metadata.

form // InputForm

To put data into the form one Curries the form with an Association (or list of rules) containing strings.

submittedform = form[<|"quantity" -> "1 m", "real" -> "12.1", "phone" -> "617 555 1234"|>]

Once the strings are interpreted they form the second argument of the FormObject (while the inputs are added to the metadata in the first field).

submittedform//InputForm

Data can be extracted by using Setting

Setting[submittedform]

Interpreter

Interpreter is the superfunction that turns strings into Wolfram Language objects. It is somewhat similar to SemanticInterpretation, but it introduces the concept of types.

There are two large families of Interpreters: semantic and structured. Structured interpreters work entirely in the kernel, while semantic interpreters need to talk to our servers (note that this is a non-local operation even when CloudDeploy-ed).

Interpreter["Number"]["one"]

Interpreter["Number"]["1"]

Interpreter["SemanticNumber"]["one"]

There is actually another type of Interpreter which allows for computation before the result is returned.

Interpreter["ComputedNumber"]["one plus three"]

Restrictions

Restrictions can be used to only allow some results (a range of numbers, a string pattern, quantities with a given unit…)

Interpreter[Restricted["Number", {1, 10}]]["11"]

Restrictions are also useful to inform the interpretation when some Information might be missing.

Interpreter[Restricted["PhoneNumber", (Entity["Italy", Entity["Country","Italy"], "Entity[\"Country\", \"Italy\"]", "country"])]]["333 12345678"]

Restrictions are designed to be semantically relevant to the type. They somewhat limit the flexibility of having a pure function test, but are able to provide helpful messages when the result is outside the specification.

Interpreter[Restricted["Number", {0,6,2}]]["5"]

Interpreter[Restricted["Number", {0,6,1.5}]]["5"]

Interpreter[Restricted["Number", {0,6,3}]]["5"]

Interpreter[Restricted["City", (Entity["Spain", Entity["Country","Spain"], "Entity[\"Country\", \"Spain\"]", "country"])]]["oslo"]

FormFunction

$Permissions="Public";

FormFunctions are made of a FormObject specification, a pure function and a return type (optional).

form = CloudDeploy @ FormFunction[
{"city" -> Restricted["City", (Entity["Italy", Entity["Country","Italy"], "Entity[\"Country\", \"Italy\"]", "country"])]},
GeoGraphics[#city] &,
"PNG"
]

APIFunctions have the exact same signature.

api = CloudDeploy @ APIFunction[
{"city" -> Restricted["City", (Entity["Italy", Entity["Country","Italy"], "Entity[\"Country\", \"Italy\"]", "country"])]},
GeoGraphics[#city] &,
"PNG"
]

A deployed APIFunction can be used by other web-pages or programs…

CloudDeploy @ ExportForm[
TemplateApply[
"<h1>Rome</h1><img src='``'/>", 
URLBuild[api, {"city" -> "Rome"}]
], 
"HTMLFragment"
]

Just like FormObject APIFunction and FormFunction can be programmatically provided input.

APIFunction["fib" -> "Number", Fibonacci[#fib]&][<|"fib" -> "30"|>]

The metadata in fields can be used to specify various behavior

FormFunction[{"age" -> <|
"Interpreter"-> Restricted["Integer", {18, 100}],
"Control" -> Slider,
"Default" -> 21,
"Input" -> "21",
"Label" -> "Age",
"Help"-> "Insert your current age"
|>},
	Identity
] //CloudDeploy

Several options can be used to tweak the appearance of the form:

CloudDeploy @ FormFunction[
{"Website" -> <|
"Interpreter" -> "SemanticURL", 
"Input" -> "Wikipedia"|>
}, 
Composition[HTTPRedirect, First], 
AppearanceRules-><|
"Title" -> "WebGo!", "Description" -> "Write the name of any website, and we will take you there!", "SubmitLabel" -> "Go!"
|>,
FormTheme->"Red"
]

ItemLayout can be set to Horizontal, Vertical or Inline.

CloudDeploy @ FormFunction[{
"FieldA" -> "String",
"FieldB" -> "String",
"FieldC" -> "String",
"FieldD" -> "String"
},
Identity,
AppearanceRules-> <|"ItemLayout" -> "Vertical"|>
]

When evaluated in the frontend, FormFunction behaves like Input or Dialog and lock the kernel. This can come in handy, because FormFunction, like any other Function returns a value that can be used in further computation.

If[
FormFunction[{"Liking", "do you like bananas?" }-> "Boolean", First][], , 
FormFunction[{"HaveCat", "do you have a cat?"} -> {"yes" -> , "no" -> }, First][]
]

Similar results can be achieved in version 10.0.1 using the new multistage for syntax

ff = FormFunction[{
<|"country" -> "Country"|>,
<|"city" -> <|
"Interpreter" -> Restricted["City", #country], "Help" -> TemplateApply["Insert a city in ``", #country]
|>
|> &
}, Identity
][]

Exercises

1. Create a FormFunction that asks for a city and displays one piece of information about that city (current weather, population, time of sunset, coordinates…). Bonus points: make a page that displays tourist information in a nice grid (FormFunction[…, Grid[…], “HTML”])

2. Create a form that uses at least three different controls.

3. Create a FormFunction that is comes already filled with a random number and deploy it. Now try making a label that contains your country name, or the current date.

4. Create an Instagram clone with a multistep form. First upload an image, second choose a filter, then return the filtered image. Bonus points for displaying a preview in the second step.